////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//  Filename:    GreenLightGuts.cpp                                           //
//  Developer:   Tommy Riddle                                                 //
//  Create date: 04/13/2007                                                   //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//  Description: This is the class implementation for the slot gut of the     //
//               Australian Slot machine called Green Light.                  //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
//        Copyright (c) 2007 - Realtime Gaming.  All rights reserved.         //
////////////////////////////////////////////////////////////////////////////////

#include "GreenLightGuts.h"
#include "getrand.h"

////////////////////////////////////////////////////////////////////////////
/// GreenLightGuts::m_aLineWinSpinResults
///
/// This array is used to quickly get the SpinResult for the number of
/// icons.  It is indexed by wheel icon and then count.  So to get the
/// SpinResult for 5 Aces, use the following code snippet:
///
///		SpinResult result = m_aLineWinSpinResults[ ICON_ACE ][ 5 ];
///
/// In this example result would be set to EVT_FIVE_ACE.
////////////////////////////////////////////////////////////////////////////
GreenLightGuts::SpinResult GreenLightGuts::m_aLineWinSpinResults[][6] = 
{
// Number of icons:			0			1				2					3						4					5
/* ICON_NINE */			{	EVT_LOSER,	EVT_LOSER,		EVT_LOSER,			EVT_THREE_NINE,			EVT_FOUR_NINE,		EVT_FIVE_NINE		},
/* ICON_TEN */			{	EVT_LOSER,	EVT_LOSER,		EVT_LOSER,			EVT_THREE_TEN,			EVT_FOUR_TEN,		EVT_FIVE_TEN		},
/* ICON_JACK */			{	EVT_LOSER,	EVT_LOSER,		EVT_LOSER,			EVT_THREE_JACK,			EVT_FOUR_JACK,		EVT_FIVE_JACK		},
/* ICON_QUEEN */		{	EVT_LOSER,	EVT_LOSER,		EVT_LOSER,			EVT_THREE_QUEEN,		EVT_FOUR_QUEEN,		EVT_FIVE_QUEEN		},
/* ICON_KING */			{	EVT_LOSER,	EVT_LOSER,		EVT_LOSER,			EVT_THREE_KING,			EVT_FOUR_KING,		EVT_FIVE_KING		},
/* ICON_ACE */			{	EVT_LOSER,	EVT_LOSER,		EVT_LOSER,			EVT_THREE_ACE,			EVT_FOUR_ACE,		EVT_FIVE_ACE		},
/* ICON_CHAMPAGNE */	{	EVT_LOSER,	EVT_LOSER,		EVT_TWO_CHAMPAGNE,	EVT_THREE_CHAMPAGNE,	EVT_FOUR_CHAMPAGNE,	EVT_FIVE_CHAMPAGNE	},
/* ICON_TROPHY */		{	EVT_LOSER,	EVT_LOSER,		EVT_TWO_TROPHY,		EVT_THREE_TROPHY,		EVT_FOUR_TROPHY,	EVT_FIVE_TROPHY		},
/* ICON_WOMAN */		{	EVT_LOSER,	EVT_LOSER,		EVT_TWO_WOMAN,		EVT_THREE_WOMAN,		EVT_FOUR_WOMAN,		EVT_FIVE_WOMAN		},
/* ICON_CAR */			{	EVT_LOSER,	EVT_LOSER,		EVT_TWO_CAR,		EVT_THREE_CAR,			EVT_FOUR_CAR,		EVT_FIVE_CAR		},
/* ICON_LIGHT */		{	EVT_LOSER,	EVT_LOSER,		EVT_LOSER,			EVT_LOSER,				EVT_LOSER,			EVT_LOSER			},
/* ICON_DRIVER */		{	EVT_LOSER,	EVT_LOSER,		EVT_TWO_DRIVER,		EVT_THREE_DRIVER,		EVT_FOUR_DRIVER,	EVT_FIVE_DRIVER		}
};

////////////////////////////////////////////////////////////////////////////
/// GreenLightGuts::m_aGreenLightSpinResults
///
/// This array is used to quickly get the SpinResult for the number of
/// icons.  It is indexed by icon count only, since it only stores
/// Green Light results.  So to get the SpinResult for 5 Green Lights,
/// use the following code snippet:
///
///		SpinResult result = m_aScatterWinSpinResults[ 5 ];
///
/// In this example result would be set to EVT_FIVE_GREEN_LIGHT.
////////////////////////////////////////////////////////////////////////////
GreenLightGuts::SpinResult GreenLightGuts::m_aGreenLightSpinResults[6] = 
{
	// ICON_LIGHT			   Number of Icons
	EVT_LOSER,				// 0
	EVT_SCATTER_NEAR_MISS,	// 1
	EVT_TWO_GREEN_LIGHT,	// 2
	EVT_THREE_GREEN_LIGHT,	// 3
	EVT_FOUR_GREEN_LIGHT,	// 4
	EVT_FIVE_GREEN_LIGHT	// 5
};

////////////////////////////////////////////////////////////////////////////
/// GreenLightGuts::m_aWhiteLightSpinResults
///
/// This array is used to quickly get the SpinResult for the number of
/// icons.  It is indexed by icon count only, since it only stores
/// White Light results.  So to get the SpinResult for 5 White Lights,
/// use the following code snippet:
///
///		SpinResult result = m_aScatterWinSpinResults[ 5 ];
///
/// In this example result would be set to EVT_FIVE_WHITE_LIGHT.
////////////////////////////////////////////////////////////////////////////
GreenLightGuts::SpinResult GreenLightGuts::m_aWhiteLightSpinResults[6] = 
{
	// ICON_LIGHT			   Number of Icons
	EVT_LOSER,				// 0
	EVT_ONE_WHITE_LIGHT,	// 1
	EVT_TWO_WHITE_LIGHT,	// 2
	EVT_THREE_WHITE_LIGHT,	// 3
	EVT_FOUR_WHITE_LIGHT,	// 4
	EVT_FIVE_WHITE_LIGHT	// 5
};

////////////////////////////////////////////////////////////////////////////
/// @fn 	 GreenLightGuts::GreenLightGuts()   
///
/// This is the default constructor for the class.
////////////////////////////////////////////////////////////////////////////
GreenLightGuts::GreenLightGuts()
{
	// If this doesn't compile it means that someone probably added
	// another icon type but did not update the
	// m_aLineWinSpinResults array. 
	C_ASSERT( countof( m_aLineWinSpinResults ) == NUM_VALID_ICONS );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 GreenLightGuts::~GreenLightGuts()
///
/// Destructor for GreenLightGuts. 
////////////////////////////////////////////////////////////////////////////
GreenLightGuts::~GreenLightGuts()
{
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 GreenLightGuts::SpinResult GreenLightGuts::getDriverLineWinResult() const
///
/// @param   stops			The array of stop positions for the line.
///
/// @return	 SpinResult		A payout from the SpinResult enumeration.
///
/// This helper method is used to calculate the Driver line win payout.
////////////////////////////////////////////////////////////////////////////
GreenLightGuts::SpinResult GreenLightGuts::getDriverLineWinResult( uint8* stops ) const
{
	// Count how many Driver icons we have at the beginning of the line.
	for ( uint8 i = 0; i < getReelCount(); i++ )
    {
		// If this icon is not a Driver...
		if ( getIcon( getGameModeId(), i, stops[i] )->iconid != ICON_DRIVER )
		{
			// then we have our total.
			break;
		}
    }

	// Use the array to look up the spin result for i Driver icons.
	return lookupLineWinSpinResult( ICON_DRIVER, i );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 GreenLightGuts::SpinResult GreenLightGuts::getLineWinResultWithPossibleSubstitution( uint8* stops, uint8 testIcon, bool& bSubstitution ) const
///
/// @param   stops			The array of stop positions for the line.
/// @param   testIcon		The icon we are looking for, typically the first icon on the line.
/// @param   bSubstitution	An output parameter that is set if a subsitution is used.
///
/// @return	 SpinResult		A payout from the SpinResult enumeration.
///
/// This helper method is used to calculate the line win payout with a possible substitution.
////////////////////////////////////////////////////////////////////////////
GreenLightGuts::SpinResult GreenLightGuts::getLineWinResultWithPossibleSubstitution(
	uint8* stops, uint8 line, uint8 testIcon, bool& bSubstitution )
{
	// Initialize the out parameter
	bSubstitution = false;

	// Loop through the remaining reels until the streak is over
	for ( int i = 1; i < getReelCount(); i++ )
    {
		// See what this icon is.
		uint8 currentIcon = getIcon( getGameModeId(), i, stops[i] )->iconid;

		// If the current icon is a Driver...
		if ( currentIcon == ICON_DRIVER )
		{
			// then use it as a substitution symbol and continue on.
			bSubstitution = true;
			continue;
		}
		// Otherwise if the test icon is a Driver...
		else if ( testIcon == ICON_DRIVER )
		{
			// then we still have a substitution.  In this case the line
			// started out with a Driver.
			bSubstitution = true;

			// We have now found our first icon that is not a Driver.  All
			// of the rest of the icons will have to match this one now for the
			// streak to continue, so change our test icon.
			testIcon = currentIcon;
			continue;
		}

		// If the current icon is not the test icon...
        if ( currentIcon != testIcon )
        {
			// then the streak is over so break out of the loop
			break;
        }
    }

	// If we made it all the way through and the testIcon is still a Driver...
	if ( testIcon == ICON_DRIVER )
	{
		// then this must be a 5 Driver win, so reset the substitution flag.
		bSubstitution = false;
	}

	// Use the array to look up the spin result for i testIcons.
	return lookupLineWinSpinResult( testIcon, i );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 bool GreenLightGuts::doesDriverLineWinPayMore( SpinResult srDriver, SpinResult srOther, bool bSubstitution ) const
///
/// @param   srDriver       The spin result to calculate the Driver payout for.
/// @param   srOther		The spin result to calculate the other payout for.
/// @param   bSubstitution	Set if we should use the substitution multiplier with the other payout.
///
/// @return	 bool			True if the Driver win is larger.
///
/// This helper method is used to see which payout is larger.
////////////////////////////////////////////////////////////////////////////
bool GreenLightGuts::doesDriverLineWinPayMore( SpinResult srDriver,
	SpinResult srOther, bool bSubstitution ) const
{
	// Lookup the payout for the Driver line win.
	long nDriverLineWinPayout = getPayout( srDriver )->payoutAmount;

	// See what the total payout for the other line win is without the substitution
	// multiplier (if any).
	long nOtherLineWinPayout = getPayout( srOther )->payoutAmount;

	// If we had a substitution...
	if ( bSubstitution )
	{
		// then factor in the multiplier.
		nOtherLineWinPayout *= MULTIPLIER_SUBSTITUTION;
	}

	// Return true if the Driver payout is bigger.
    return ( nDriverLineWinPayout > nOtherLineWinPayout );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 GreenLightGuts::classifyStops()   
///
/// @param   stops      The array of stop positions for the line.
/// @param   bet        The value of the line bet.
/// @param   line       The index of the line.
///
/// @return	 uint8      A payout from the SpinResult enumeration as a uint8.    
///
/// This helper method is used to calculate the payout on a line.
////////////////////////////////////////////////////////////////////////////
void GreenLightGuts::classifyStops( SpinLine* spinline, uint8* stops, uint8 bet, uint8 line,
	uint8& rtnresult, uint8& multiplier )
{
	// Track the Driver result separately since it might be changed to a substitution.
	SpinResult srDriverLineWinResult = getDriverLineWinResult( stops );

	// Now see what the payout is if we use any Driver we find as substitution symbols.
	// Start with the icon on the first reel.
	uint8 testIcon = getIcon( getGameModeId(), 0, stops[0] )->iconid;

	// Declare a local variable to track whether or not we found a substitution.
	bool bSubstitution = false;

	// See what the spin result is if we used any Driver icons as substitution symbols.
	SpinResult srResultWithPossibleSubstitution =
		getLineWinResultWithPossibleSubstitution( stops, line, testIcon, bSubstitution );

	// Use the larger payout between the regular Driver evaluation compared to
	// the evaluation with the possible substitution.
    if ( doesDriverLineWinPayMore( srDriverLineWinResult,
		srResultWithPossibleSubstitution, bSubstitution ) )
	{
		// The Driver icons by themselves pay out more so use the Driver result.
		rtnresult = srDriverLineWinResult;
	}
	else
	{
		// Otherwise the result with the substitution (if any) paid more so use it.
		rtnresult = srResultWithPossibleSubstitution;

		// If we did actually have a substitution and a winning spin...
		if ( bSubstitution && srResultWithPossibleSubstitution != EVT_LOSER )
		{
			// then add a note and change the multiplier.
			spinline->addNote( NOTE_SUBSTITUTION );
			multiplier = MULTIPLIER_SUBSTITUTION;
		}
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn		GreenLightGuts::tryForJackpot()
///
///	This function tries to win the jackpot and adds the scatter if we hit
/// the jackpot.
////////////////////////////////////////////////////////////////////////////
void GreenLightGuts::tryForJackpot( SlotSpin* spin )
{
	// If we are in the first paid spin (NOT a bonus spin)...
	if ( getGameModeId() == MODE_NORMAL ) 
	{
		// then try for the Random Progressive Jackpot
		// If we hit the jackpot...
		if ( getRandomProgressiveJackpotResult( getNumLinesBet() ) || 
             m_bForceRandomJackpot == true )
		{
			// then add a jackpot scatter to the spin.
			spin->addScatter( 0,
				getPayout( EVT_RANDOM_PROGRESSIVE_WINNER )->payoutAmount, 1,
				true, EVT_RANDOM_PROGRESSIVE_WINNER, 0 );
		}
    }
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 GreenLightGuts::getWhiteLightResult()   
///
/// @param   spin						The current spin object.
/// @param   nNumWhiteLightIcons		The number of white light icons that we find.
/// @param   nWhiteLightPositions		The bit mask for the white light positions that we find.
///
/// @return	 SpinResult			A payout from the SpinResult enumeration.
///
/// This helper method is used to evaluate the spin for scatters.
////////////////////////////////////////////////////////////////////////////
GreenLightGuts::SpinResult GreenLightGuts::getWhiteLightResult( SlotSpin* spin, int32& nNumWhiteLightIcons, int32& nWhiteLightPositions ) const
{
	// Initialize the counter and scatter positions.
	nNumWhiteLightIcons = 0;
	nWhiteLightPositions = 0;

	// Loop through each reel.
  	for( int reel = 0; reel < getReelCount(); reel++ )
	{
		// The position bit for the current icon in case it needs to be stored in a mask.
		uint8 position = reel;

		// Loop through each row in the wheel house.
		for( int row = 0; row < 3; row++ )
		{
			// Get the current stop.
			uint8 stop = relativeWheelPosition( getGameModeId(), reel,
				spin->getStops()[ reel ], row );

			// Get the icon for the current stop.
			uint8 currentIcon = getIcon( getGameModeId(), reel, stop )->iconid;
			
			// If the icon is the one we are looking for...
			if ( currentIcon == ICON_LIGHT )
			{
				// then increase the icon count
				nNumWhiteLightIcons++;

				// Set the bit in the position mask for this stop.
				nWhiteLightPositions |= 1 << position;
			}

			// Move to the next bit in the position mask.
            position += 5;
		}
	}

	// Use the array to look up the spin result for nNumWhiteLightIcons.
	return lookupWhiteLightSpinResult( nNumWhiteLightIcons );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 GreenLightGuts::getGreenLightResult()   
///
/// @param   spin						The current spin object.
/// @param   nNumWhiteLightIcons		The number of white light icons that we already found.
/// @param   nWhiteLightPositions		The bit mask for the white light positions that we already found.
/// @param   nNumGreenLightIcons		The number of green light icons that we find.
/// @param   nGreenLightPositions		The bit mask for the green light positions that we find.
///
/// @return	 SpinResult			A payout from the SpinResult enumeration.
///
/// This helper method is used to evaluate the spin for scatters.
////////////////////////////////////////////////////////////////////////////
GreenLightGuts::SpinResult GreenLightGuts::getGreenLightResult( SlotSpin* spin, int32 nNumWhiteLightIcons, int32 nWhiteLightPositions, int32& nNumGreenLightIcons, int32& nGreenLightPositions ) const
{
	// Initialize the number of green lights.
	nNumGreenLightIcons = 0;

	// If we had 2 white lights...
	if ( nNumWhiteLightIcons == 2 )
	{
		// then set the number of green lights based on the probability table.
		// If we are in the free spin mode...
		if ( getGameModeId() == MODE_FREE_SPIN )
		{
			// then use the free spin probability table for 2 white lights.
			nNumGreenLightIcons = getRandomValueFromProbabilityTable( PROB_TABLE_GREEN_LIGHTS_FROM_2_WHITES_FREE );
		}
		// Otherwise...
		else
		{
			// use the base game probability table for 2 white lights.
			nNumGreenLightIcons = getRandomValueFromProbabilityTable( PROB_TABLE_GREEN_LIGHTS_FROM_2_WHITES_BASE );
		}
	}
	// If we had 3 white lights...
	else if ( nNumWhiteLightIcons == 3 )
	{
		// then set the number of green lights based on the probability table.
		// If we are in the free spin mode...
		if ( getGameModeId() == MODE_FREE_SPIN )
		{
			// then use the free spin probability table for 3 white lights.
			nNumGreenLightIcons = getRandomValueFromProbabilityTable( PROB_TABLE_GREEN_LIGHTS_FROM_3_WHITES_FREE );
		}
		// Otherwise...
		else
		{
			// use the base game probability table for 3 white lights.
			nNumGreenLightIcons = getRandomValueFromProbabilityTable( PROB_TABLE_GREEN_LIGHTS_FROM_3_WHITES_BASE );
		}
	}
	// If we had 4 white lights...
	else if ( nNumWhiteLightIcons == 4 )
	{
		// then set the number of green lights based on the probability table.
		// If we are in the free spin mode...
		if ( getGameModeId() == MODE_FREE_SPIN )
		{
			// then use the free spin probability table for 4 white lights.
			nNumGreenLightIcons = getRandomValueFromProbabilityTable( PROB_TABLE_GREEN_LIGHTS_FROM_4_WHITES_FREE );
		}
		// Otherwise...
		else
		{
			// use the base game probability table for 4 white lights.
			nNumGreenLightIcons = getRandomValueFromProbabilityTable( PROB_TABLE_GREEN_LIGHTS_FROM_4_WHITES_BASE );
		}
	}
	// If we had 5 white lights...
	else if ( nNumWhiteLightIcons == 5 )
	{
		// then set the number of green lights based on the probability table.
		// If we are in the free spin mode...
		if ( getGameModeId() == MODE_FREE_SPIN )
		{
			// then use the free spin probability table for 5 white lights.
			nNumGreenLightIcons = getRandomValueFromProbabilityTable( PROB_TABLE_GREEN_LIGHTS_FROM_5_WHITES_FREE );
		}
		// Otherwise...
		else
		{
			// use the base game probability table for 5 white lights.
			nNumGreenLightIcons = getRandomValueFromProbabilityTable( PROB_TABLE_GREEN_LIGHTS_FROM_5_WHITES_BASE );
		}
	}

	// Initialize the scatter positions.
	nGreenLightPositions = nWhiteLightPositions;

	// Now see how many position bits we need to turn off.
	int32 nNumToTurnOff = nNumWhiteLightIcons - nNumGreenLightIcons;

	// This should never happen.
	ASSERT( nNumToTurnOff >= 0 );
	ASSERT( nNumToTurnOff <= 5 );

	// Loop until we don't have any more position bits to turn off...
	while ( nNumToTurnOff )
	{
		// See which bit to turn off next from the remaining set bits.
		int32 nNextBitToTurnOff = getRandomLessThan( nNumGreenLightIcons + nNumToTurnOff ) + 1;
		
		// We are going to need a counter...
		int32 nNumBitsFoundSoFar = 0;
		
		// and a mask.
		int32 nMask = 1;

		// This should never happen.
		ASSERT( nNumBitsFoundSoFar < nNextBitToTurnOff );

		// Loop	until we find that bit...
		while ( nNumBitsFoundSoFar < nNextBitToTurnOff )
		{
			// If the current bit is set...
			if ( nGreenLightPositions & nMask )
			{
				// then we found another bit.
				nNumBitsFoundSoFar++;

				// If it is the one we are looking for..
				if ( nNumBitsFoundSoFar == nNextBitToTurnOff )
				{
					// then get out of the loop.
					break;
				}
			}

			// Push the mask bit over 1.
			nMask <<= 1;

			// Infinite loop check.  If the mask bit wraps off the end...
			if ( nMask == 0 )
			{
				// BTW- this should never happen either.
				ASSERT( false );

				// then we some how got into an infinite loop.  Just break out.
				break;
			}
		}

		// Now the mask bit should be on the one we are supposed to turn off.
		// Turn off just that one bit.
		nGreenLightPositions &= ~nMask;

		// We have one less to turn off now.
		nNumToTurnOff--;
	}

	// Use the array to look up the spin result for nNumGreenLightIcons.
	return lookupGreenLightSpinResult( nNumGreenLightIcons );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 GreenLightGuts::addWhiteLightScatter()
///
/// This function determines if the current spin wins a scatter payment and
/// adds the scatter.
////////////////////////////////////////////////////////////////////////////
void GreenLightGuts::addWhiteLightScatter( SlotSpin* spin, SpinResult srWhiteLightResult, int32 nWhiteLightPositions )
{
	// If we got the scatter win...
	if ( srWhiteLightResult != EVT_LOSER )
	{
		// Add a scatter to the spin.
		spin->addScatter( nWhiteLightPositions, 0, 0, false, srWhiteLightResult, 0 );
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 GreenLightGuts::addGreenLightScatter()
///
/// This function determines if the current spin wins a scatter payment and
/// adds the scatter.
////////////////////////////////////////////////////////////////////////////
void GreenLightGuts::addGreenLightScatter( SlotSpin* spin, SpinResult srGreenLightResult, int32 nGreenLightPositions )
{
	// If we got the scatter win or a near miss...
	if ( srGreenLightResult != EVT_LOSER )
	{
		// then initialize our multiplier for scatters.
		int32 multiplier = 1;

		// If we are in free spin mode...
		if ( getGameModeId() == MODE_FREE_SPIN )
		{
			// then use the free spin scatter multiplier.
			multiplier = MULTIPLIER_FREE_SPIN;
		}

		// Get the base payout for the scatter.
		int32 nPayout = getPayout( srGreenLightResult )->payoutAmount;
		
		// Scatters payout based on the total bet, so factor in the number of bet lines.
		nPayout *= getNumLinesBet();

		// Add the payout (with multiplier) to the total win for this spin.
		increaseTotalWin( nPayout * multiplier );
    
		// Add a scatter to the spin.
		SlotScatter* pScatter = spin->addScatter( nGreenLightPositions,
			nPayout * getBetPerLine(), multiplier, false, srGreenLightResult, 0 );

		// If the free spin multiplier was used...
		if ( multiplier == MULTIPLIER_FREE_SPIN )
		{
			// then add the free spin note.
			pScatter->addNote( NOTE_FREE_SPIN );
		}
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 GreenLightGuts::addFeatureTriggerScatter()
///
/// This function determines if the current spin triggered the bonus game
/// and adds the appropriate scatter.
////////////////////////////////////////////////////////////////////////////
void GreenLightGuts::addFeatureTriggerScatter( SlotSpin* spin, int32 nNumGreenLightIcons, int32 nGreenLightPositions )
{
	// Initialize the race results
	for ( uint8 nRace = 1; nRace <= 2; nRace++ )
	{
		for ( uint8 eCar = RF_CAR_1; eCar < NUM_RACE_CAR_CHOICES; eCar++ )
		{
			setRaceResult( nRace, eCar, 0 );
		}
	}

	// Initialize the current race.
	setCurrentRace( 0 );

	// Initialize the player car.
	setPlayerCar( RF_INVALID );

	// If there were at least 3 scatter icons...
	if ( nNumGreenLightIcons >= 3 )
	{
		// then add the racing feature.
		spin->addScatter( nGreenLightPositions, 0, 0, false, EVT_RACING_FEATURE, 0 );

		// If we have not reached the maximum payout already...
		if ( getTotalWin() < getFeatureGameCap() )
		{
			// then the game can't complete until the bonus game is done.
			setGameCanComplete( false );
			setBonusGameState( BONUS_GAME_ACTIVE );
			setCurrentRace( 1 );
		}
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 GreenLightGuts::classifyScatters( SlotSpin* spin )
///
/// @param   spin       Pointer to the spin with stop data.
///
/// This is an override of the framework method.  
/// It determines if the current spin merits free spins or scatter payments.
////////////////////////////////////////////////////////////////////////////
void GreenLightGuts::classifyScatters( SlotSpin* spin )
{	
	// Try for the Random Progressive Jackpot
	tryForJackpot( spin );

	// Declare some local variables to track the scatter positions.
	int32 nWhiteLightPositions = 0;
	int32 nNumWhiteLightIcons = 0;
	int32 nGreenLightPositions = 0;
	int32 nNumGreenLightIcons = 0;

	// See if there is a white light scatter.
	SpinResult srWhiteLightResult = getWhiteLightResult( spin, nNumWhiteLightIcons, nWhiteLightPositions );

	// See if there is a green light scatter.
	SpinResult srGreenLightResult = getGreenLightResult( spin, nNumWhiteLightIcons, nWhiteLightPositions, nNumGreenLightIcons, nGreenLightPositions );

	// Add the white light scatter, if any.
	addWhiteLightScatter( spin, srWhiteLightResult, nWhiteLightPositions );

	// Add the green light scatter, if any.
	addGreenLightScatter( spin, srGreenLightResult, nGreenLightPositions );
	
	// Add the feature trigger scatter, if any.
	addFeatureTriggerScatter( spin, nNumGreenLightIcons, nGreenLightPositions );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 GreenLightGuts::isValidGuts()   
///
/// @return	 bool8      True if gut is valid.    
///
/// This helper method is used to see if the current gut ID is valid.
////////////////////////////////////////////////////////////////////////////
bool8 GreenLightGuts::isValidGuts()
{
	// Make sure that the m_aLineWinSpinResults array has an entry for each reel,
	// and an additional entry in case there are 0 icons on the line.
	// Note:  we can't do this in the constructor because we have to call
	// getReelCount.
	ASSERT( countof( m_aLineWinSpinResults[0] ) == getReelCount( MODE_NORMAL ) + 1 );

	// Make sure that the m_aGreenLightSpinResults array has an entry for each reel,
	// and an additional entry in case there are 0 icons on the line.
	ASSERT( countof( m_aGreenLightSpinResults ) == getReelCount( MODE_NORMAL ) + 1 );

	// Make sure that the m_aWhiteLightSpinResults array has an entry for each reel,
	// and an additional entry in case there are 0 icons on the line.
	ASSERT( countof( m_aWhiteLightSpinResults ) == getReelCount( MODE_NORMAL ) + 1 );

	// Now make sure that both game modes have the same number of reels.
	ASSERT( getReelCount( MODE_NORMAL ) == getReelCount( MODE_FREE_SPIN ) );

	// Finally just check to see if the gut is within the right range.
    return ( getGutsId() >= 1900 && getGutsId() < 2000 );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 GreenLightGuts::getGameModeMultiplierAndNoteId( uint8& nGameModeMultiplier, uint8& nNoteId )
///
/// @param   nGameModeMultiplier	The multiplier for the game mode.
/// @param   nNoteId				The note ID for the game mode.
///
/// This helper method is used to get the game mode multiplier and note ID.
////////////////////////////////////////////////////////////////////////////
void GreenLightGuts::getGameModeMultiplierAndNoteId( uint8& nGameModeMultiplier,
	uint8& nNoteId )
{
	// If we are in free spin mode...
    if ( getGameModeId() == MODE_FREE_SPIN )
    {
		// then return the free spin multiplier...
        nGameModeMultiplier = MULTIPLIER_FREE_SPIN;

		// and the free spin note.
        nNoteId = NOTE_FREE_SPIN;
    }
	// Otherwise...
    else
    {
		// just defer to the base class.
        SlotGuts::getGameModeMultiplierAndNoteId( nGameModeMultiplier, nNoteId );
    }
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 GreenLightGuts::hasSavedGames()
///
/// @param   bool8   True/False indicating if this machine supports saved gamees.
///
/// This is a framework override to determine if this machine supports saved games.
////////////////////////////////////////////////////////////////////////////
bool8 GreenLightGuts::hasSavedGames()
{
	// We support saved games so always return true.
	return true;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 GreenLightGuts::getRaceResult()
///
/// This helper method is used to get the subgame data that tracks 
/// what position the car finished in for the specified race.
////////////////////////////////////////////////////////////////////////////
uint8 GreenLightGuts::getRaceResult( uint8 nRace, uint8 nCar )
{
	// Just return the subgame data.
	return m_subGame->getData( calculateRaceResultIndex( nRace, nCar ) );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 GreenLightGuts::setRaceResult()
///
/// This helper method is used to set the subgame data that tracks 
/// what position the car finished in for the specified race.
////////////////////////////////////////////////////////////////////////////
void GreenLightGuts::setRaceResult( uint8 nRace, uint8 nCar, uint8 nResult )
{
	// Just store the new value in the subgame data.
    m_subGame->update( calculateRaceResultIndex( nRace, nCar ), nResult );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 GreenLightGuts::::determineRaceResults( )
///
/// This function is called to determine the race results.
////////////////////////////////////////////////////////////////////////////
uint8 GreenLightGuts::determineRaceResults( RaceCarConstants ePlayerCar )
{
	// Declare a 1 based array to keep up with whether each finish position
	// has been assigned to a car yet.  This is 1 based so that element 1 is
	// for 1st place, etc.  Element 0 is ignored.
	bool abResultTaken[ NUM_RACE_CAR_CHOICES + 1 ];

	// Initialize all of the elements to false since they have not been
	// assigned to any cars yet.
	for ( int32 n = 1; n <= NUM_RACE_CAR_CHOICES; n++ )
	{
		abResultTaken[ n ] = false;
	}

	// Use the proper race probability table to get the player result.
	uint8 nPlayerResult;

	// If we are on the first race...
	if ( getCurrentRace() == 1 )
	{
		// then use the 1st race probability table to get the player result.
		nPlayerResult = getRandomValueFromProbabilityTable( PROB_TABLE_RACE_1_FINISHES );
	}
	// Otherwise...
	else
	{
		// we must be on the 2nd race.
		ASSERT( getCurrentRace() == 2 );

		// Use the 2nd race probability table to get the player result.
		nPlayerResult = getRandomValueFromProbabilityTable( PROB_TABLE_RACE_2_FINISHES );
	}

	// Store the player result.
	setRaceResult( getCurrentRace(), ePlayerCar, nPlayerResult );

	// Now the position of the player car that was assigned by the probability
	// table has been used, so set the flag in the array.  That way none of the
	// other cars will be able to use it.  We don't want any ties in this
	// race.
	abResultTaken[ nPlayerResult ] = true;

	// Keep up with how many results are still available.  This number is
	// used as an upper bound for the random number generator so that we
	// don't waste our time getting random numbers for positions that are
	// already being used.	We now only have 4 positions left.
	int32 nResultsAvailable = 4;

	// Loop through all of the computer cars and assign each one a finish
	// position.
	for ( uint8 nCar = RF_CAR_1; nCar < NUM_RACE_CAR_CHOICES; nCar++ )
	{
		// If this car is a computer car...
		if ( nCar != ePlayerCar )
		{
			// then get a random result to use based on how many results are left.
			// In the first pass there will be 4 results available, and this
			// code will calculate a random number from 1 to 4... 1 meaning that
			// we will use the first available result that we find.
			int32 nNextResultToUse = getRandomLessThan( nResultsAvailable ) + 1;

			// Now keep up with how many empty slots we have found so far.
			int32 nAvailableResultsFound = 0;

			// Loop through the results array.
			for ( int32 nResult = 1; nResult <= NUM_RACE_CAR_CHOICES; nResult++ )
			{
				// If this position is not taken already...
				if ( !abResultTaken[ nResult ] )
				{
					// then we found the next available result.
					nAvailableResultsFound++;

					// If this is the random empty slot that we are looking for...
					if ( nAvailableResultsFound == nNextResultToUse )
					{
						// then use it.
						break;
					}
				}
			}

			// Sanity check... these asserts should never happen.
			ASSERT( nResult >= 1 );
			ASSERT( nResult <= NUM_RACE_CAR_CHOICES );
			ASSERT( !abResultTaken[ nResult ] );

			// Store the random result for the current car.
			setRaceResult( getCurrentRace(), nCar, nResult );

			// Mark this result as no longer available.
			abResultTaken[ nResult ] = true;

			// Finally reduce the number of results remaining.
			nResultsAvailable--;
		}
	}

	// Sanity check... these asserts should never happen.
	ASSERT( nResultsAvailable == 0 );
	ASSERT( getRaceResult( getCurrentRace(), ePlayerCar ) == nPlayerResult );
	ASSERT( abResultTaken[ 1 ] );
	ASSERT( abResultTaken[ 2 ] );
	ASSERT( abResultTaken[ 3 ] );
	ASSERT( abResultTaken[ 4 ] );
	ASSERT( abResultTaken[ 5 ] );
	ASSERT( getRaceResult( getCurrentRace(), RF_CAR_1 ) != getRaceResult( getCurrentRace(), RF_CAR_2 ) );
	ASSERT( getRaceResult( getCurrentRace(), RF_CAR_1 ) != getRaceResult( getCurrentRace(), RF_CAR_3 ) );
	ASSERT( getRaceResult( getCurrentRace(), RF_CAR_1 ) != getRaceResult( getCurrentRace(), RF_CAR_4 ) );
	ASSERT( getRaceResult( getCurrentRace(), RF_CAR_1 ) != getRaceResult( getCurrentRace(), RF_CAR_5 ) );
	ASSERT( getRaceResult( getCurrentRace(), RF_CAR_2 ) != getRaceResult( getCurrentRace(), RF_CAR_3 ) );
	ASSERT( getRaceResult( getCurrentRace(), RF_CAR_2 ) != getRaceResult( getCurrentRace(), RF_CAR_4 ) );
	ASSERT( getRaceResult( getCurrentRace(), RF_CAR_2 ) != getRaceResult( getCurrentRace(), RF_CAR_5 ) );
	ASSERT( getRaceResult( getCurrentRace(), RF_CAR_3 ) != getRaceResult( getCurrentRace(), RF_CAR_4 ) );
	ASSERT( getRaceResult( getCurrentRace(), RF_CAR_3 ) != getRaceResult( getCurrentRace(), RF_CAR_5 ) );
	ASSERT( getRaceResult( getCurrentRace(), RF_CAR_4 ) != getRaceResult( getCurrentRace(), RF_CAR_5 ) );

	// Finally return the player result.
	return nPlayerResult;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 GreenLightGuts::getCurrentRace()
///
/// This helper method is used to get the subgame data that tracks 
/// what race we are on.
////////////////////////////////////////////////////////////////////////////
uint8 GreenLightGuts::getCurrentRace()
{
	// Just return the subgame data.
	return m_subGame->getData( SGDIDX_CURRENT_RACE );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 GreenLightGuts::setCurrentRace()
///
/// This helper method is used to set the subgame data that tracks 
/// what race we are on.
////////////////////////////////////////////////////////////////////////////
void GreenLightGuts::setCurrentRace( uint8 nRace )
{
	// Just store the new value in the subgame data.
    m_subGame->update( SGDIDX_CURRENT_RACE, nRace );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 GreenLightGuts::getPlayerCar()
///
/// This helper method is used to get the subgame data that stores 
/// which car the player picked.
////////////////////////////////////////////////////////////////////////////
GreenLightGuts::RaceCarConstants GreenLightGuts::getPlayerCar()
{
	// Just return the subgame data.
	return (RaceCarConstants) m_subGame->getData( SGDIDX_PLAYER_CAR );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 GreenLightGuts::setPlayerCar()
///
/// This helper method is used to set the subgame data that stores 
/// which car the player picked.
////////////////////////////////////////////////////////////////////////////
void GreenLightGuts::setPlayerCar( RaceCarConstants rcPlayerCar )
{
	// Just store the new value in the subgame data.
    m_subGame->update( SGDIDX_PLAYER_CAR, rcPlayerCar );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 GreenLightGuts::addFreeSpinsBonus()
///
/// This helper method is used to add the free spins bonus. 
////////////////////////////////////////////////////////////////////////////
void GreenLightGuts::addFreeSpinsBonus( uint8 nResult )
{
	// See how many free spins they get.
	int32 nFreeSpins = 0;

	// If they finished in 1st place...
	if ( nResult == 1 )
	{
		// then they get 25 free spins.
		nFreeSpins = 25;
	}
	// If they finished in 2nd place...
	else if ( nResult == 2 )
	{
		// then they get 20 free spins.
		nFreeSpins = 20;
	}
	// If they finished in 3rd place...
	else if ( nResult == 3 )
	{
		// then they get 15 free spins.
		nFreeSpins = 15;
	}
	// If they finished in 4th place...
	else if ( nResult == 4 )
	{
		// then they get 10 free spins.
		nFreeSpins = 10;
	}
	// Otherwise...
	else
	{
		// they must have finished in 5th place.
		ASSERT( nResult == 5 );

		// They get 5 free spins.
		nFreeSpins = 5;
	}

	// Add the free spins.
	incrementFreeSpins( nFreeSpins );

	// Add the bonus game result.
	addBonusGame( BONUS_GAME_RACING_FEATURE, 0, 0, 0, 0, nFreeSpins );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 GreenLightGuts::addMultiplierBonus()
///
/// This helper method is used to add the multiplier bonus. 
////////////////////////////////////////////////////////////////////////////
void GreenLightGuts::addMultiplierBonus( uint8 nResult )
{
	// See what they win.
	int32 nPrize = 0;

	// If they finished in 1st place...
	if ( nResult == 1 )
	{
		// then they get 100.
		nPrize = 100;
	}
	// If they finished in 2nd place...
	else if ( nResult == 2 )
	{
		// then they get 50.
		nPrize = 50;
	}
	// If they finished in 3rd place...
	else if ( nResult == 3 )
	{
		// then they get 25.
		nPrize = 25;
	}
	// If they finished in 4th place...
	else if ( nResult == 4 )
	{
		// then they get 10.
		nPrize = 10;
	}
	// Otherwise...
	else
	{
		// they must have finished in 5th place.
		ASSERT( nResult == 5 );

		// They get 5.
		nPrize = 5;
	}

	// Calculate the bonus prize.
	int32 nBasePayoutAmount = getBetPerLine() * getNumLinesBet() * nPrize;
	
	// Initialize the multiplier.
	int32 nTotalMultiplier = 1;

	// If we are in free spin mode...
	if ( getGameModeId() == MODE_FREE_SPIN )
	{
		// then use the free spin scatter multiplier.
		nTotalMultiplier = MULTIPLIER_FREE_SPIN;
	}

	// Calculate the total payout.
	int32 nTotalBonusPayoutAmount = nBasePayoutAmount * nTotalMultiplier;

	// Add the bonus game result.
	BonusGame* pBonusGame = addBonusGame( BONUS_GAME_RACING_FEATURE, 1, nBasePayoutAmount, nTotalMultiplier, nTotalBonusPayoutAmount, 0 );

	// If the free spin multiplier was used...
	if ( nTotalMultiplier == MULTIPLIER_FREE_SPIN )
	{
		// then add the free spin note.
		pBonusGame->addNote( NOTE_FREE_SPIN );
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 GreenLightGuts::processClientData()
///
/// @param   bonusGameType      The current type of bonus game.
/// @param   bonusDataCount     The number of integers passed from the client.
/// @param   bonusData			An array of integers passed from the client.
/// 
/// This is an override of the framework method.
/// 
////////////////////////////////////////////////////////////////////////////
bool8 GreenLightGuts::processClientData( uint8 bonusGameType, int32 bonusDataCount, int32 *bonusData )
{
	// Make sure the bonus game is active (in case somebody is trying to hack us)
	if ( getBonusGameState() == BONUS_GAME_INACTIVE )
	{
		return false;
	}

	// Make sure the bonus game type is correct (in case somebody is trying to hack us)
	if ( bonusGameType != BONUS_GAME_RACING_FEATURE )
	{
		return false;
	}

	// Make sure the data count is correct (in case somebody is trying to hack us)
	if ( bonusDataCount != 1 )
	{
		return false;
	}

	// Make sure the pointer is not NULL.  I'm not sure how this could happen, but we
	// don't want to crash if it does.
	if ( bonusData == NULL )
	{
		ASSERT( false );
		return false;
	}

	// Make sure we have bonus games.
	if ( getCurrentRace() == 0 )
	{
		return false;
	}

	// Store the car that they picked in a local variable.
	RaceCarConstants ePlayerCar = (RaceCarConstants)(*bonusData);

	// Make sure the player's car is valid (in case somebody is trying to hack us)
	if ( ePlayerCar < 0 || ePlayerCar >= NUM_RACE_CAR_CHOICES )
	{
		return false;
	}

	// If this is the first race...
	if ( getCurrentRace() == 1 )
	{
		// Store the player's car in sub-game data.  We really don't care what they pick,
		// but we need to save it for the client so they can use the correct car in a 
		// restored game.
		setPlayerCar( ePlayerCar );
	}
	// Otherwise if they didn't choose the same car...
	else if ( getPlayerCar() != ePlayerCar )
	{
		// then somebody must be trying to hack us.  Our client doesn't let you change
		// cars in between races.
		return false;
	}

	// Initialize a local to hold the players result.
	uint8 nResult = determineRaceResults( ePlayerCar );

	// If this is the first race...
	if ( getCurrentRace() == 1 )
	{
		// If they finished in the top 2...
		if ( nResult == 1 || nResult == 2 )
		{
			// then move on to the next race.
			setCurrentRace( 2 );
		}
		// Otherwise...
		else
		{
			// add the free spins bonus.
			addFreeSpinsBonus( nResult );

			// Make sure we are in free spin mode.
			// Note:  We might already be in free spin mode, but that is OK.
			setGameModeId( MODE_FREE_SPIN );

			// We are done.
			setGameCanComplete( true );
			setBonusGameState( BONUS_GAME_INACTIVE );

			// We should never get into here if we hit the max payout since it should
			// have skipped the bonus game.
			ASSERT( !getMaxPayoutAwarded() );
		}
	}
	// Otherwise...
	else
	{
		// Make sure we are in fact on race 2.
		ASSERT( getCurrentRace() == 2 );

		// Make sure they finished 1st or 2nd in the first race.
		ASSERT( getRaceResult( 1, getPlayerCar() ) == 1 || getRaceResult( 1, getPlayerCar() ) == 2 );

		// Add the free spins bonus from the first race.
		addFreeSpinsBonus( getRaceResult( 1, getPlayerCar() ) );

		// Add the multiplier bonus.
		addMultiplierBonus( nResult );

		// Now we are done.
		setGameCanComplete( true );
		setBonusGameState( BONUS_GAME_INACTIVE );

		// If the bonus prize did not put us over the max payout...
		if ( !getMaxPayoutAwarded() )
		{
			// then make sure we are in free spin mode.
			// Note:  We might already be in free spin mode, but that is OK.
			setGameModeId( MODE_FREE_SPIN );
		}
	}

	return true;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 GreenLightGuts::Debug_SetLoserStops( uint8* pStops )
///
/// @param   pStops		The stops to set.
///
/// Base class override, returns a losing spin triggered by the debug tool
////////////////////////////////////////////////////////////////////////////
void GreenLightGuts::Debug_SetLoserStops( uint8* pStops )
{
	pStops[0]=8;
	pStops[1]=0;
	pStops[2]=0;
	pStops[3]=0;
	pStops[4]=0;
}

